package org.maxgroundstation.simulation.service;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Abstract class for a MAX robot service.
 */
public abstract class Service {

	// Data members
	protected ServiceCommunication comm;
	protected String name;
	protected String format;
	protected String description;
	protected Map<String, String> parameters;
	protected ConcurrentLinkedQueue<ServiceSubscriber> subscribers;
	
	/**
	 * Constructor.
	 * @param comm the service communication.
	 */
	public Service(ServiceCommunication comm) {
		this.comm = comm;
		name = "";
		format = "";
		description = "";
		parameters = new HashMap<String, String>();
		subscribers = new ConcurrentLinkedQueue<ServiceSubscriber>();
	}
	
	/**
	 * Gets the service communication.
	 * @return service communication.
	 */
	public ServiceCommunication getServiceCommunication() {
		return comm;
	}
	
	/**
	 * Gets the unique service ID.
	 * @return service ID.
	 */
	public abstract String getID();
	
	/**
	 * Checks if service is a default service.
	 * @return true if default service.
	 */
	public abstract boolean isDefaultService();
	
	/**
	 * Gets the service response format.
	 * @return format.
	 */
	public String getFormat() {
		return format;
	}
	
	/**
	 * Gets the service name.
	 * @return the service name.
	 */
	public String getName() {
		return name;
	}
	
	/**
	 * Gets the service description.
	 * @return service description.
	 */
	public String getDescription() {
		return description;
	}
	
	/**
	 * Gets the map of parameter keys and values.
	 * @return map of parameter keys and values.
	 */
	public Map<String, String> getParameters() {
		return parameters;
	}
	
	/**
	 * Handles a data frame response for the service.
	 * @param dataFrame the response data frame.
	 * @param commType the communication type (TCP or UDP).
	 * @throws Exception if error handling response.
	 */
	public abstract void handleResponse(ServiceDataFrame dataFrame, int commType) throws Exception;
	
	/**
	 * Shuts down the service.
	 */
	public abstract void shutDown();
	
	/**
	 * Checks if the service is subscribable.
	 * @return true if subscribable.
	 */
	public abstract boolean isSubscribable();
	
	/**
	 * Subscribes to the service.
	 * @param clientID the client ID.
	 * @param commType the communication type.
	 */
	public void subscribe(String clientID, int commType) {
		if (isSubscribable()) {
			// Check if subscriber list contains client ID.
			boolean containsSubscriber = false;
			Iterator<ServiceSubscriber> i = subscribers.iterator();
			while (i.hasNext()) {
				if (i.next().getClientID().equals(clientID)) containsSubscriber = true;
			}
			
			// If not, then add the subscriber to the list.
			if (!containsSubscriber) {
				comm.log("Client: "  + clientID + " subscribing to service " + getName());
				subscribers.add(new ServiceSubscriber(clientID, commType));
			}
		}
	}
	
	/**
	 * Unsubscribe to the service.
	 * @param clientID the client ID.
	 */
	public void unsubscribe(String clientID) {
		if (isSubscribable()) {
			// Remove any subscribers in the list that match the client ID.
			Iterator<ServiceSubscriber> i = subscribers.iterator();
			while (i.hasNext()) {
				if (i.next().getClientID().equals(clientID)) i.remove();
			}
		}
	}
}